package com.goldsprite.gsgengine.core.gameobjects;

import com.goldsprite.gsgengine.core.EnableableGameLoop;
import com.goldsprite.gsgengine.core.components.Component;
import com.goldsprite.gsgengine.core.components.Components;
import com.goldsprite.gsgengine.core.components.Transform;
import com.goldsprite.gsgengine.core.components.monobehaviours.MonoBehaviour;
import com.goldsprite.gsgengine.core.managers.GameObjectManager;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class GameObject extends EnableableGameLoop {
	public final Transform transform;
	private final Map<Class<? extends Component>, List<Component>> components = new ConcurrentHashMap<>();
	private final List<MonoBehaviour> monoBehaviours = new ArrayList<>();
	private final String uuid;

	private GameObject(String uuid) {
		this.uuid = uuid;
		GameObjectManager.getInstance().instantiateEntity(this);
		transform = addComponent(Transform.class);
	}

	public static GameObject instantiate() {
		return new GameObject(GameObjectManager.getInstance().getUuidSeed());
	}

	public <T extends Component> T addComponent(T component) {
		component.setGameObject(this);
		components.computeIfAbsent(component.getClass(), k -> new ArrayList<>()).add(component);
		if (component instanceof MonoBehaviour) {
			monoBehaviours.add((MonoBehaviour) component);
		}
		return component;
	}

	public <T extends Component> T addComponent(Class<T> componentClass) {
		try {
			return addComponent(componentClass.getConstructor().newInstance());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public boolean removeComponent(Class<? extends Component> componentClass) {
		List<Component> list = components.get(componentClass);
		if (list != null && !list.isEmpty()) {
			Component component = list.remove(list.size() - 1);
			if (component instanceof MonoBehaviour) {
				monoBehaviours.remove(component);
			}
			if (list.isEmpty()) components.remove(componentClass);
			return true;
		}
		return false;
	}

	public boolean removeComponents(Class<? extends Component> componentClass) {
		List<Component> removed = components.remove(componentClass);
		if (removed != null) {
			removed.forEach(component -> {
				if (component instanceof MonoBehaviour) {
					monoBehaviours.remove(component);
				}
			});
			return true;
		}
		return false;
	}

	public <T extends Component> T getComponent(Class<T> componentClass) {
		return componentClass.cast(components.getOrDefault(componentClass, Collections.emptyList()).stream().findFirst().orElse(null));
	}

	public List<? extends Component> getComponents(Class<? extends Component> componentClass) {
		return components.getOrDefault(componentClass, Collections.emptyList());
	}

	public String getUuid() {
		return uuid;
	}

	@Override
	public void start() {
		monoBehaviours.forEach(MonoBehaviour::start);
	}

	@Override
	public void fixedUpdate(float fixedDelta) {
		monoBehaviours.forEach(mono -> mono.fixedUpdate(fixedDelta));
	}

	@Override
	public void update(float delta) {
		monoBehaviours.forEach(mono -> mono.fixedUpdate(delta));
	}

	@Override
	public void destroy() {
		components.clear();
		monoBehaviours.clear();
		GameObjectManager.getInstance().destroyGameObject(this);
	}
}
